﻿#include "qlib/geo/pin.h"
#include "qlib/utils/font.h"

#include <iostream>
#include <sstream>
#include <string>
#include <vector>

namespace qlib {
Pin::Pin() {
  layer = 0;
  datatype = 0;

  box.resize(2);
}
Pin::Pin(std::string _text, Vec2d _origin, double _rotation, double _magnification, bool _x_reflection, uint32_t _layer, uint32_t _datatype) {
  text = _text;
  rotation = _rotation;
  origin = _origin;
  magnification = _magnification;
  x_reflection = _x_reflection;
  layer = _layer;
  datatype = _datatype;

  layer = 0;
  datatype = 0;

  box.resize(2);
}

Pin::Pin(const Pin& oth) {
  operator=(oth);
}
Pin& Pin::operator=(const Pin& other) {
  const Pin* pin = &other;
  this->layer = pin->layer;
  this->datatype = pin->datatype;
  this->text = pin->text;
  this->fontSize = pin->fontSize;
  this->origin = pin->origin;
  this->anchor = pin->anchor;
  this->origin = pin->origin;
  this->magnification = pin->magnification;
  this->x_reflection = pin->x_reflection;
  this->repetition = pin->repetition;
  this->rotation = pin->rotation;

  properties.resize(other.properties.size());
  for (size_t i = 0; i < properties.size(); i++) {
    if (other.properties[i]) {
      properties[i] = Property::create();
      *properties[i] = *other.properties[i];
    }
  }

  lines = other.lines;

  return *this;
}

void Pin::copy(std::shared_ptr<Pin> pin) {
  operator=(*pin);
}
void Pin::copy(std::shared_ptr<Label> label) {
  this->layer = label->layer;
  this->datatype = label->datatype;
  this->text = label->text;
  this->fontSize = label->fontSize;
  this->origin = label->origin;
  this->anchor = label->anchor;
  this->origin = label->origin;
  this->magnification = label->magnification;
  this->x_reflection = label->x_reflection;
  this->repetition = label->repetition;
  this->properties = label->properties;
  this->rotation = label->rotation;
}
std::shared_ptr<Pin> Pin::copy() const {
  auto res = Pin::create();
  *res = *this;

  return res;
}

void Pin::apply_repetition(PinPtrArray& result) {
  if (repetition.type == RepetitionType::None)
    return;

  Array<Vec2d> offsets = {};
  repetition.get_offsets(offsets);
  repetition = Repetition();

  // Skip first offset (0, 0)
  Vec2d* offset_p = offsets.data() + 1;
  vector_reserve_now(result, offsets.size() - 1);

  for (auto& offset : offsets) {
    auto pin = Pin::create();
    *pin = *this;
    pin->translate(offset.x, offset.y);
    result.push_back(pin);
  }

  for (uint64_t offset_count = offsets.size() - 1; offset_count > 0; offset_count--) {
    auto pin = Pin::create();
    *pin = *this;
    pin->translate(offset_p->x, offset_p->y);
    offset_p++;
    result.push_back(pin);
  }

  offsets.clear();
  return;
}

void Pin::transform(double mag, bool x_refl, double rot, const Vec2d orig) {
  const int r1 = x_refl ? -1 : 1;
  const double crot = cos(rot);
  const double srot = sin(rot);
  const double x = origin.x;
  const double y = origin.y;
  origin.x = orig.x + mag * (x * crot - r1 * y * srot);
  origin.y = orig.y + mag * (x * srot + r1 * y * crot);
  rotation = r1 * rotation + rot;
  magnification *= mag;
  x_reflection ^= x_refl;
}

Vec2dArray Pin::bounding_box_origin() {
  Vec2dArray res(2);
  Vec2d& min = res[0];
  Vec2d& max = res[1];
  min.x = min.y = DBL_MAX;
  max.x = max.y = -DBL_MAX;

  qlib::Vec2d size = {1e-6, 1e-6};
  min = origin - size;
  max = origin + size;
  return res;
}

Vec2dArray Pin::bounding_box() {
  Vec2dArray res(2);
  Vec2d& min = res[0];
  Vec2d& max = res[1];
  min.x = min.y = DBL_MAX;
  max.x = max.y = -DBL_MAX;

  qlib::Vec2d size = {0.00001, 0.00001};
  min = origin - size;
  max = origin + size;

  // 计算未应用重复下的包围盒
  auto points = get_texts();
  const Vec2d* p = points.data();
  for (uint64_t num = points.size(); num > 0; num--, p++) {
    if (p->x < min.x)
      min.x = p->x;
    if (p->x > max.x)
      max.x = p->x;
    if (p->y < min.y)
      min.y = p->y;
    if (p->y > max.y)
      max.y = p->y;
  }

  return res;
}

Vec2dArray Pin::get_texts() {
  Vec2dArray res;
  // 提
  auto texts = split_str(text, ',');
  // auto num_str = get_text(texts[0], pos, fontSize, 0.0, Anchor::O);
  // auto name_str = get_text(texts[1], pos, fontSize, 0.0, Anchor::O);
  // auto type_str = get_text(texts[2], pos, fontSize, 0.0, Anchor::O);

  auto pos = origin;
  for (size_t i = 0; i < texts.size(); i++) {
    auto& t = texts[i];
    pos.y += 15;
    if (i == 0 && !is_display_id)
      continue;
    if (i == 1 && !is_display_name)
      continue;
    if (i == 2 && !is_display_type)
      continue;
    auto text_lines = get_text(t, pos, fontSize, 0.0, Anchor::O);
    res.insert(res.end(), text_lines.begin(), text_lines.end());
  }

  // 应用旋转属性
  double ca = cos(rotation);
  double sa = sin(rotation);

  Vec2d vt;
  for (size_t i = 0; i < res.size(); i++) {
    vt = res[i] - origin;
    res[i].x = vt.x * ca - vt.y * sa + origin.x;
    res[i].y = vt.x * sa + vt.y * ca + origin.y;
  }

  return res;
};

Vec2dArray Pin::get_line() {
  Vec2dArray res;
  res.resize(2);
  res[0] = origin;
  res[1] = origin + Vec2d{length, 0};

  // 应用旋转属性
  double ca = cos(rotation);
  double sa = sin(rotation);

  Vec2d vt;
  for (size_t i = 0; i < res.size(); i++) {
    vt = res[i] - origin;
    res[i].x = vt.x * ca - vt.y * sa + origin.x;
    res[i].y = vt.x * sa + vt.y * ca + origin.y;
  }

  return res;
};

std::vector<std::string> splitString(const std::string& input) {
  std::vector<std::string> result;
  std::stringstream ss(input);
  std::string item;

  while (std::getline(ss, item, ',')) {
    result.push_back(item);
  }

  int old_size = result.size();
  for (size_t i = old_size; i < 3; i++) {
    result.push_back("");
  }

  return result;
}

PinType strToPinType(std::string str) {
  if (str == "B Pin") {
    return PinType::B_Pin;
  }

  if (str == "Q Pin") {
    return PinType::Q_Pin;
  }

  if (str == "Res Pin") {
    return PinType::Res_Pin;
  }

  if (str == "X Pin") {
    return PinType::X_Pin;
  }

  if (str == "Z Pin") {
    return PinType::Z_Pin;
  }

  if (str == "P Pin") {
    return PinType::P_Pin;
  }

  assert(0);
}

std::string pinTypeToStr(PinType type) {
  if (type == PinType::B_Pin) {
    return "B Pin";
  }

  if (type == PinType::Q_Pin) {
    return "Q Pin";
  }

  if (type == PinType::Res_Pin) {
    return "Res Pin";
  }

  if (type == PinType::X_Pin) {
    return "X Pin";
  }

  if (type == PinType::Z_Pin) {
    return "Z Pin";
  }

  if (type == PinType::P_Pin) {
    return "P Pin";
  }

  assert(0);
}

PinType Pin::getPinType() {
  auto str_arr = splitString(text);
  assert(str_arr.size() == 3);

  return strToPinType(str_arr[2]);
}
void Pin::setPinType(PinType type) {
  auto str_arr = splitString(text);
  assert(str_arr.size() == 3);

  text = (str_arr[0] + "," + str_arr[1] + "," + pinTypeToStr(type));
}

uint32_t Pin::getId() {
  auto str_arr = splitString(text);
  assert(str_arr.size() == 3);

  return std::stod(str_arr[0]);
}
void Pin::setId(uint32_t id) {
  auto str_arr = splitString(text);
  assert(str_arr.size() == 3);

  text = (std::to_string(id) + "," + str_arr[1] + "," + str_arr[2]);
}

std::string Pin::getName() {
  auto str_arr = splitString(text);
  assert(str_arr.size() == 3);

  return str_arr[1];
}
void Pin::setName(std::string name) {
  auto str_arr = splitString(text);
  assert(str_arr.size() == 3);

  text = (str_arr[0] + "," + name + "," + str_arr[2]);
}

void Pin::rotate(double angle, Vec2d _center) {
  rotation += angle;

  origin -= _center;
  auto t = origin;

  double ca = cos(angle);
  double sa = sin(angle);

  origin.x = t.x * ca - t.y * sa + _center.x;
  origin.y = t.x * sa + t.y * ca + _center.y;
}
void Pin::translate(double dx, double dy) {
  origin += {dx, dy};
}
void Pin::mirror(qlib::Vec2d p0, qlib::Vec2d p1) {
  Vec2d v = p1 - p0;
  double tmp = v.length_sq();
  if (tmp == 0)
    return;

  Vec2d r = v * (2 / tmp);
  Vec2d p2 = p0 * 2;

  Vec2d* p = &origin;
  *p = v * (*p - p0).inner(r) - *p + p2;

  x_reflection = !x_reflection;
}

Polygon::s_ptr Pin::to_polygon() {
  auto res = Polygon::create();
  res->points = get_texts();
  res->layer = layer;
  res->datatype = datatype;
  return res;
}

std::vector<Polygon::s_ptr> Pin::to_polygons() {
  return {to_polygon()};
}

PinParts Pin::get_parts() {
  auto res = std::make_tuple(origin);
  return res;
}

Vec2dArray Pin::query_parts(GeoPartType type, uint32_t id) {
  auto parts = get_parts();
  if (type == GeoPartType::Center)
    return Vec2dArray{std::get<0>(parts)};
  return Vec2dArray{};
}

double Pin::getRotation() {
  return rotation;
}

void Pin::setRotation(double _rotation) {
  rotation = _rotation;
}

void Pin::savePropertiesGDS() {
  set_gds_property(properties, 0, std::to_string(fontSize));  // 字体大小
  set_gds_property(properties, 1, "");                        // 是否为关键点
  set_gds_property(properties, 3, "Pin");                     // 是否为引脚

  set_gds_property(properties, 10, std::to_string(length));
  set_gds_property(properties, 11, std::to_string(int(is_display_type)));
  set_gds_property(properties, 12, std::to_string(int(is_display_name)));
  set_gds_property(properties, 13, std::to_string(int(is_display_id)));
}
void Pin::loadPropertiesGDS() {
  if (get_gds_property(properties, 0)) {
    std::string fontSizeS = (*get_gds_property(properties, 0))[1]->string;
    fontSize = std::stod(fontSizeS);
  }

  if (get_gds_property(properties, 10)) {
    std::string lengthS = (*get_gds_property(properties, 10))[1]->string;
    length = std::stod(lengthS);
  }

  if (get_gds_property(properties, 11)) {
    std::string is_display_typeS = (*get_gds_property(properties, 11))[1]->string;
    is_display_type = std::stoi(is_display_typeS);
  }

  if (get_gds_property(properties, 12)) {
    std::string is_display_nameS = (*get_gds_property(properties, 12))[1]->string;
    is_display_name = std::stoi(is_display_nameS);
  }

  if (get_gds_property(properties, 13)) {
    std::string is_display_idS = (*get_gds_property(properties, 13))[1]->string;
    is_display_id = std::stoi(is_display_idS);
  }
}

void Pin::savePropertiesOAS() {}
void Pin::loadPropertiesOAS() {}

}  // namespace qlib